﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ManagedNite;

namespace MediaPortal.Plugins.Sense
{
    public class NavigatorControl : XnMBroadcaster
    {
        // inner controls
        protected XnMSteadyDetector steadyDetector;
        protected XnMSwipeDetector swipeDetector;
        protected ScrollFilter scrollFilter;
        protected XnMSelectableSlider2D slider;

        // inner control containers
        protected XnMPointDenoiser pointDenoiser;
        protected XnMFlowRouter flowRouter;
        protected XnMBroadcaster swipeControl;
        protected XnMBroadcaster steadyControl;
        protected XnMBroadcaster scrollControl;
        protected XnMBroadcaster pushControl;

        // scroll variables
        protected int scrollPrimer = 0;
        Direction lastDirection = Direction.Illegal;

        public event Action<SensorEvent> Action;

        public NavigatorControl()
            : base()
        {

            this.swipeDetector = new XnMSwipeDetector(false);
            this.swipeDetector.Activate += new EventHandler(OnSwipeDetectorActivate);
            this.swipeDetector.MotionSpeedThreshold = 0.20f;    //0.25	float
            this.swipeDetector.MotionTime = 160;	            //350	uint
            this.swipeDetector.XAngleThreshold = 35.00f;	    //25.0	float
            this.swipeDetector.YAngleThreshold = 35.00f;	    //20.0	float
            this.swipeDetector.GeneralSwipe += new EventHandler<SwipeDetectorGeneralEventArgs>(OnGeneralSwipe);

            this.slider = new XnMSelectableSlider2D(1, 1);
            this.slider.HysteresisRatio = 0;
            this.slider.ItemSelected += new EventHandler<SelectableSlider2DSelectEventArgs>(OnSliderItemSelected);

            this.steadyDetector = new XnMSteadyDetector();
            this.steadyDetector.Activate += new EventHandler(OnSteadyDetectorActivate);
            this.steadyDetector.DetectionDuration = steadyDurationSwipe;
            this.steadyDetector.MaximumVelocity = 0.01f;
            this.steadyDetector.Steady += new EventHandler<SteadyEventArgs>(OnSteady);

            this.scrollFilter = new ScrollFilter();
            this.scrollFilter.Scroll += new EventHandler<ScrollFilterEventArgs>(OnScroll);
            this.scrollFilter.ScrollReleased += new Action<bool>(OnScrollReleased);
            
            this.swipeControl = new XnMBroadcaster();
            this.swipeControl.Activate += new EventHandler(OnSwipeControlActivation);
            this.swipeControl.Deactivate += new EventHandler(OnSwipeControlDeactivation);
            this.swipeControl.AddListener(swipeDetector);
            this.swipeControl.AddListener(slider);

            this.steadyControl = new XnMBroadcaster();
            this.steadyControl.Activate += new EventHandler(OnSteadyControlActivation);
            this.steadyControl.Deactivate += new EventHandler(OnSteadyControlDeactivation);
            this.steadyControl.AddListener(steadyDetector);
            this.steadyControl.AddListener(swipeDetector);
            this.steadyControl.AddListener(slider);

            this.scrollControl = new XnMBroadcaster();
            this.scrollControl.Activate += new EventHandler(OnScrollControlActivation);
            this.scrollControl.Deactivate += new EventHandler(OnScrollControlDeactivation);
            this.scrollControl.AddListener(steadyDetector);
            this.scrollControl.AddListener(scrollFilter);
            this.scrollControl.AddListener(slider);

            this.flowRouter = new XnMFlowRouter();

            this.pointDenoiser = new XnMPointDenoiser(2);
            this.pointDenoiser.Activate += new EventHandler(OnPointDenoiserActivate);
            this.pointDenoiser.AddListener(this.flowRouter);

            this.AddListener(this.pointDenoiser);
        }

        #region Public Properties
        
        public virtual int DefaultSteadyDuration
        {
            get
            {
                return this.steadyDurationSwipe;
            }
            set
            {
                this.steadyDurationSwipe = value;
            }
        } protected int steadyDurationSwipe = 90;

        public virtual int ScrollCancelSteadyDuration
        {
            get
            {
                return this.steadyDurationScroll;
            }
            set
            {
                this.steadyDurationScroll = value;
            }
        } protected int steadyDurationScroll = 2000;

        public virtual int ScrollSensitivity
        {
            get
            {
                return this.scrollFilter.Sensitivity;
            }
            set
            {
                this.scrollFilter.Sensitivity = value;
            }
        } 

        public virtual bool ScrollInverted
        {
            get
            {
                return this.scrollFilter.Inverted;
            }
            set
            {
                this.scrollFilter.Inverted = value;
            }
        }

        #endregion

        #region Control Eventhandlers

        #region Steady Eventhandlers

        protected virtual void OnSteadyControlActivation(object sender, EventArgs e)
        {
            scrollPrimer = 0;
            this.flowRouter.ClearQueue();
            this.steadyControl.ClearQueue();
            Logger.Debug("SteadyControl Active.");
        }

        protected virtual void OnSteadyControlDeactivation(object sender, EventArgs e)
        {
            Logger.Debug("SteadyControl Deactived.");
        }

        protected virtual void OnSteady(object sender, SteadyEventArgs e)
        {
            if (this.flowRouter.ActiveControl == scrollControl)
            {
                this.flowRouter.SetActiveControl(steadyControl);
                return;
            }

            if (this.flowRouter.ActiveControl != swipeControl)
            {
                lastDirection = Direction.Illegal;
                this.flowRouter.SetActiveControl(swipeControl);
            }
        }

        #endregion

        #region Swipe Eventhandlers

        protected virtual void OnSwipeControlActivation(object sender, EventArgs e)
        {
            this.flowRouter.ClearQueue();
            this.swipeControl.ClearQueue();
            Logger.Debug("SwipeControl Active. {0}");
        }

        protected virtual void OnSwipeControlDeactivation(object sender, EventArgs e)
        {
            Logger.Debug("SwipeControl Deactived.");
        }

        protected virtual void OnGeneralSwipe(object sender, SwipeDetectorGeneralEventArgs e)
        {
            Direction direction = e.SelectDirection;
            if (this.flowRouter.ActiveControl == swipeControl)
            {
                switch (direction)
                {
                    case Direction.Up:
                        raiseAction(SensorEvent.Up);
                        break;
                    case Direction.Down:
                        raiseAction(SensorEvent.Down);
                        break;
                    case Direction.Left:
                        raiseAction(SensorEvent.Left);
                        break;
                    case Direction.Right:
                        raiseAction(SensorEvent.Right);
                        break;
                }

                lastDirection = direction;
                this.flowRouter.SetActiveControl(steadyControl);
            }
            else if (this.flowRouter.ActiveControl == steadyControl)
            {
                evaluateScrollPrimer(direction);
            }
        }

        #endregion

        #region Scroll Eventhandlers

        protected virtual void OnScrollControlActivation(object sender, EventArgs e)
        {
            this.steadyDetector.DetectionDuration = steadyDurationScroll;
            this.flowRouter.ClearQueue();
            this.scrollControl.ClearQueue();
            bool scrollOnX = (this.lastDirection == Direction.Left || this.lastDirection == Direction.Right);
            this.scrollFilter.ScrollOnX = scrollOnX;
            Logger.Debug("ScrollControl active: {0}-axis.", scrollOnX ? "X" : "Y");
            raiseAction(SensorEvent.ScrollStart);
        }

        protected virtual void OnScrollControlDeactivation(object sender, EventArgs e)
        {
            raiseAction(SensorEvent.ScrollEnd);
            this.steadyDetector.DetectionDuration = steadyDurationSwipe;
            Logger.Debug("ScrollControl Deactived.");
        }

        protected virtual void OnScroll(object sender, ScrollFilterEventArgs e)
        {
            switch (e.Direction)
            {
                case Direction.Up:
                    raiseAction(SensorEvent.Up, e.Multiplier);
                    break;
                case Direction.Down:
                    raiseAction(SensorEvent.Down, e.Multiplier);
                    break;
                case Direction.Left:
                    raiseAction(SensorEvent.Left, e.Multiplier);
                    break;
                case Direction.Right:
                    raiseAction(SensorEvent.Right, e.Multiplier);
                    break;
            }
        }

        protected virtual void OnScrollReleased(bool released)
        {
            raiseAction(released ? SensorEvent.ScrollReleased : SensorEvent.ScrollConnected);
        }

        #endregion

        #region Other

        protected virtual void OnSwipeDetectorActivate(object sender, EventArgs e)
        {
            swipeDetector.Reset();
        }

        protected virtual void OnSteadyDetectorActivate(object sender, EventArgs e)
        {
            steadyDetector.Reset();
        }

        protected virtual void OnPointDenoiserActivate(object sender, EventArgs e)
        {
            this.flowRouter.SetActiveControl(this.swipeControl);
        }

        protected virtual void OnSliderItemSelected(object sender, SelectableSlider2DSelectEventArgs e)
        {
            Logger.Debug("SliderItemSelected: Direction={0}", e.SelectDirection);
            if (this.flowRouter.ActiveControl == this.swipeControl)
            {
                if (e.SelectDirection == Direction.Forward)
                {
                    raiseAction(SensorEvent.Select);
                }
                else
                {
                    raiseAction(SensorEvent.Back);
                }

                this.flowRouter.SetActiveControl(steadyControl);
            }
        }

        #endregion

        #endregion

        #region Protected methods

        protected virtual void evaluateScrollPrimer(Direction direction)
        {
            if ((lastDirection == Direction.Left || lastDirection == Direction.Right) && (direction == Direction.Left || direction == Direction.Right))
            {

                if (this.scrollPrimer == 4)
                {
                    scrollPrimer = 0;
                    this.flowRouter.SetActiveControl(scrollControl);
                }
                else if (lastDirection != direction)
                {
                    scrollPrimer++;
                }
                lastDirection = direction;
            }
            else if ((lastDirection == Direction.Up || lastDirection == Direction.Down) && (direction == Direction.Up || direction == Direction.Down))
            {
                if (this.scrollPrimer == 3)
                {
                    scrollPrimer = 0;
                    this.flowRouter.SetActiveControl(scrollControl);
                }
                else if (lastDirection != direction)
                {
                    scrollPrimer++;
                }
                lastDirection = direction;
            }
            else if (scrollPrimer > 0)
            {
                scrollPrimer--;
            }

            Logger.Debug("ScrollPrimer: {0}", scrollPrimer);
        }

        protected virtual void raiseAction(SensorEvent control)
        {
            raiseAction(control, 1);
        }

        protected virtual void raiseAction(SensorEvent control, int multiplier)
        {
            if (Action != null)
            {
                for (int i = 0; i < multiplier; i++)
                {
                    Action(control);
                }
            }
        }

        #endregion

    }
}
